﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using EnvDTE;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace Community.DynamicsCrmWebResourceTool.Crm
{
    internal class Helper
    {
        private static OrganizationServiceProxy _serviceProxy;
        private static OrganizationServiceContext _orgContext;

        internal static OrganizationServiceProxy GetServiceProxy(string organizationServiceUrl, string username,
            string password, string domain)
        {
            ClientCredentials clientCredentials = new ClientCredentials();
            clientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(username, password, domain);

            _serviceProxy = new OrganizationServiceProxy(new Uri(organizationServiceUrl), null, clientCredentials, null);

            // This statement is required to enable early-bound type support.
            _serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());

            // The OrganizationServiceContext is an object that wraps the service
            // proxy and allows creating/updating multiple records simultaneously.
            _orgContext = new OrganizationServiceContext(_serviceProxy);

            return _serviceProxy;
        }

        internal static List<Solution> GetSolutions()
        {
            //Check whether it already exists
            QueryExpression queryUnmanagedSolutions = new QueryExpression
            {
                EntityName = Solution.EntityLogicalName,
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression()
            };
            queryUnmanagedSolutions.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false);
            EntityCollection querySolutionResults = _serviceProxy.RetrieveMultiple(queryUnmanagedSolutions);

            if (querySolutionResults.Entities.Count > 0)
            {
                //The Where() is important because a query for all solutions
                //where Type=Unmanaged returns 3 solutions. The CRM UI of a 
                //vanilla instance shows only 1 unmanaged solution: "Default". 
                //Assume "Active" and "Basic" should not be touched?
                var unmanagedSolutions = new List<Solution>(
                    querySolutionResults.Entities
                        .Select(x => x as Solution)
                        .Where(s => s.UniqueName != "Active" &&
                                    s.UniqueName != "Basic"
                        )
                    );

                return unmanagedSolutions;
            }

            return null;
        }

        internal static IEnumerable<WebResource> RetrieveWebResourcesForActiveSolution(Guid selectedSolutionId)
        {
            //The following query finds all WebResources that are SolutionComponents for
            //the ActiveSolution. Simply querying WebResources does not retrieve the desired
            //results. Additionally, when creating WebResources, you must create a SolutionComponent
            //if attaching to any unmanaged solution other than the "Default Solution."
            var webResources = from wr in _orgContext.CreateQuery<WebResource>()
                join sc in _orgContext.CreateQuery<SolutionComponent>()
                    on wr.WebResourceId equals sc.ObjectId
                where wr.IsManaged == false
                where wr.IsCustomizable.Value == true
                where sc.ComponentType.Value == (int) componenttype.WebResource
                where sc.SolutionId.Id == selectedSolutionId
                select new WebResource
                {
                    WebResourceType = wr.WebResourceType,
                    WebResourceId = wr.WebResourceId,
                    DisplayName = wr.DisplayName,
                    Name = wr.Name,
                    // Content = wr.Content, Removed to improve performance
                    Description = wr.Description
                };

            return webResources.AsEnumerable();
        }

        internal static Guid CreateWebResource(string filePath, string displayName, string description, string resourceName, Solution selectedsolution)
        {
            string type = filePath.Substring(filePath.LastIndexOf(".") + 1);

            resourceName = GetActivePublisherForTheSolution(selectedsolution.PublisherId.Id).CustomizationPrefix + "_" + resourceName;
            //Create the Web Resource.
            WebResource wr = new WebResource()
            {
                Content = GetEncodedFileContents(filePath),
                DisplayName = displayName,
                Description = description,
                LogicalName = WebResource.EntityLogicalName,
                Name = resourceName
            };

            wr.WebResourceType = new OptionSetValue((int) ResourceExtensions.ConvertStringExtension(type));

            //Special cases attributes for different web resource types.
            switch (wr.WebResourceType.Value)
            {
                case (int) ResourceExtensions.WebResourceType.Silverlight:
                    wr.SilverlightVersion = "4.0";
                    break;
            }

            Guid theGuid = _serviceProxy.Create(wr);

            //If not the "Default Solution", create a SolutionComponent to assure it gets
            //associated with the ActiveSolution. Web Resources are automatically added
            //as SolutionComponents to the Default Solution.
            if (selectedsolution.UniqueName != "Default")
            {
                AddSolutionComponentRequest scRequest = new AddSolutionComponentRequest();
                scRequest.ComponentType = (int) componenttype.WebResource;
                scRequest.SolutionUniqueName = selectedsolution.UniqueName;
                scRequest.ComponentId = theGuid;
                var response = (AddSolutionComponentResponse) _serviceProxy.Execute(scRequest);
            }

            return theGuid;
        }

        internal static void UpdateWebResource(string filePath, string displayName, string description, Guid webResourceId)
        {
            //These are the only 3 things that should (can) change.
            WebResource wr = new WebResource();
            wr.Content = GetEncodedFileContents(filePath);
            if (displayName != null) { wr.DisplayName = displayName; } //Skipping it when null. Used for direct publish
            if (description != null) { wr.Description = description; }//Skipping it when null. Used for direct publish
            wr.WebResourceId = webResourceId;
            _serviceProxy.Update(wr);
        }

        private static string GetEncodedFileContents(String pathToFile)
        {
            FileStream fs = new FileStream(pathToFile, FileMode.Open, FileAccess.Read);
            byte[] binaryData = new byte[fs.Length];
            long bytesRead = fs.Read(binaryData, 0, (int)fs.Length);
            fs.Close();
            return System.Convert.ToBase64String(binaryData, 0, binaryData.Length);
        }

        internal static Publisher GetActivePublisherForTheSolution(Guid publisherId)
        {
            var pub = (from p in _orgContext.CreateQuery<Publisher>()
                where p.PublisherId.Value == publisherId
                select new Publisher
                {
                    CustomizationPrefix = p.CustomizationPrefix
                }).FirstOrDefault();

            var x = (from p in _orgContext.CreateQuery<Publisher>()
                where p.PublisherId.Value == publisherId
                select new Publisher
                {
                    CustomizationPrefix = p.CustomizationPrefix
                }).ToList();

            return pub;
        }

        internal static void PublishWebResource(List<Guid> webResourceIds)
        {

            PublishXmlRequest publishRequest = new PublishXmlRequest();
            StringBuilder xml = new StringBuilder();
            foreach (var webResourceId in webResourceIds)
            {
                xml.Append("<webresource>{" + webResourceId + "}</webresource>");
            }

            publishRequest.ParameterXml = string.Format("<importexportxml><webresources>{0}</webresources></importexportxml>", xml.ToString());

            var response = (PublishXmlResponse)_serviceProxy.Execute(publishRequest);
        }
    }
}
